package com.quectel.business.dms.application.access.accessor.ieg.impl;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.quectel.business.dms.application.ApplicationAccessConf;
import com.quectel.business.dms.application.access.accessor.ieg.AbstractIntelligentEntranceGuardAccessor;
import com.quectel.business.dms.application.access.accessor.ieg.model.DeviceFaceSendCmd;
import com.quectel.business.dms.application.access.accessor.ieg.model.DeviceQrCodeGenCmd;
import com.quectel.business.dms.application.access.basemodel.ApplicationOriginDevice;
import com.quectel.constant.core.entranceguard.EntranceGuardPersonConstants;
import com.quectel.core.module.application.dto.ApplicationDto;
import com.quectel.core.module.device.dto.DeviceCommandTemplateDto;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.service.DeviceTypeService;
import com.quectel.core.module.entranceguard.dto.EntranceGuardPersonDto;
import com.quectel.core.module.entranceguard.service.EntranceGuardPersonService;
import com.quectel.sdk.quecentrance.QuecEntranceClientBuilder;
import com.quectel.sdk.quecentrance.entranceguard.EntranceGuardClient;
import com.quectel.sdk.quecentrance.entranceguard.model.*;
import com.quectel.util.common.DateUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.common.MyFileUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author rananxin
 * @date 2021/11/29 5:33 下午
 */
@Component
@Slf4j
public class QuecAttendanceAccessorImpl extends AbstractIntelligentEntranceGuardAccessor {

    private static final Cache<String, GroupIdCls> GROUP_CACHE =
            CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build();

    private static final String DEFAULT_VISITOR_GROUP_NAME = "default_vg:";
    private static final String DEFAULT_HOUSEHOLD_GROUP_NAME = "default_hg:";

    @DubboReference
    private DeviceTypeService deviceTypeService;
    @DubboReference
    private EntranceGuardPersonService entranceGuardPersonService;

    /**
     * 授权人员需要用到组
     */
    @Data
    private class GroupIdCls {
        private Long visitorGroupId;
        private Long householdGroupId;
    }

    @Override
    public List<AbstractIntelligentEntranceGuardAccessor.AbilityEnum> abilities() {
        return Arrays.asList(AbilityEnum.OPEN_DOOR, AbilityEnum.AUTH_PERSON);
    }

    @Override
    public boolean openDoor(List<DeviceDto> doors) {

        return false;
    }

    @Override
    public void authPerson(DeviceFaceSendCmd deviceFaceSendCmd) {
        List<DeviceFaceSendCmd.DeviceInfo> deviceInfoList = deviceFaceSendCmd.getDeviceInfoList();

        Map<String, List<EntranceGuardPersonDto>> refMap = new HashMap<>();

        for (DeviceFaceSendCmd.DeviceInfo deviceInfo : deviceInfoList) {
            try {
                DeviceDto deviceDto = deviceService.selectCacheByUniqueId(deviceInfo.getDeviceUniqueId(), deviceInfo.getTenantId());
                if (deviceDto == null) {
                    continue;
                }
                ApplicationDto application = applicationService.selectCacheById(deviceDto.getApplicationId());

                ApplicationAccessConf.TemplateOfQuecAttendanceConf templateOfQuecAttendanceConf = ApplicationAccessConf
                        .parseExtStr(ApplicationAccessConf.TemplateOfQuecAttendanceConf.class, application.getAccessConf());
                assert templateOfQuecAttendanceConf != null;

                EntranceGuardClient entranceGuardClient = QuecEntranceClientBuilder.builder()
                        .appKey(templateOfQuecAttendanceConf.getAppKey()).appSecret(templateOfQuecAttendanceConf.getAppSecret()).baseUrl(templateOfQuecAttendanceConf.getBaseUrl())
                        .build(com.quectel.sdk.quecentrance.entranceguard.EntranceGuardClient.class);

                GroupIdCls group = getGroup(deviceInfo.getDeviceUniqueId(), entranceGuardClient);

                if (DeviceFaceSendCmd.TypeEnum.ADD.getType().equals(deviceFaceSendCmd.getType())) {
                    // 授权
                    List<DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo> personFaceInfoList = deviceInfo.getPersonFaceInfoList();
                    for (DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo personFaceInfo : personFaceInfoList) {
                        String avatarUrl = entranceGuardClient.uploadAvatar(MyFileUtils.downloadToTempDir(personFaceInfo.getFaceImg(), Boolean.FALSE));
                        if (StringUtils.isBlank(avatarUrl)) {
                            log.warn("移远 门禁设备 头像上传失败 {}", JacksonUtils.toJsonString(personFaceInfo));
                            continue;
                        }

                        Long devicePersonId = null;

                        if (personFaceInfo.getPersonType() != null && personFaceInfo.getPersonType().equals(EntranceGuardPersonConstants.PersonTypeEnum.HOUSEHOLD.getType())
                                && group.getHouseholdGroupId() != null) {
                            // 住户
                            AddDeptPersonReq addPersonReq = new AddDeptPersonReq();
                            addPersonReq.setName(personFaceInfo.getName());
                            addPersonReq.setMobile(personFaceInfo.getMobile());
                            addPersonReq.setJobNumber(personFaceInfo.getJobNo());
                            addPersonReq.setAvatar(avatarUrl);
                            addPersonReq.setGroupIds(new Long[]{group.getHouseholdGroupId()});
                            devicePersonId = entranceGuardClient.addDeptPerson(addPersonReq);

                        } else if (personFaceInfo.getPersonType() != null && personFaceInfo.getPersonType().equals(EntranceGuardPersonConstants.PersonTypeEnum.VISITOR.getType())
                                && group.getVisitorGroupId() != null) {
                            // 访客
                            AddVisitorReq addVisitorReq = new AddVisitorReq();
                            addVisitorReq.setName(personFaceInfo.getName());
                            addVisitorReq.setMobile(personFaceInfo.getMobile());
                            addVisitorReq.setDateTimeFrom(DateUtils.format(personFaceInfo.getTimeStart(), DateUtils.FormatType.COMMON));
                            addVisitorReq.setDateTimeTo(DateUtils.format(personFaceInfo.getTimeEnd(), DateUtils.FormatType.COMMON));
                            addVisitorReq.setGroupIds(new Long[]{group.getVisitorGroupId()});
                            addVisitorReq.setAvatar(avatarUrl);
                            devicePersonId = entranceGuardClient.addVisitor(addVisitorReq);
                        }

                        // 更新关系表
                        List<EntranceGuardPersonDto> entranceGuardAuthDtos = refMap.computeIfAbsent(personFaceInfo.getPersonId() + "_" + personFaceInfo.getPersonType(), k -> {
                            String[] s = k.split("_");
                            return entranceGuardPersonService.queryAuthByPersonId(Byte.valueOf(s[1]), Long.valueOf(s[0]));
                        });
                        EntranceGuardPersonDto entranceGuardPersonDto = entranceGuardAuthDtos.parallelStream()
                                .filter(o -> deviceDto.getId().equals(o.getDeviceId()))
                                .findFirst().orElse(null);
                        if (entranceGuardPersonDto != null && EntranceGuardPersonConstants.AuthTypeEnum.ADD.getAuthType().equals(entranceGuardPersonDto.getAuthType())) {
                            EntranceGuardPersonDto updateObj = new EntranceGuardPersonDto();
                            updateObj.setId(entranceGuardPersonDto.getId());
                            updateObj.setIdentityAtDevice(devicePersonId != null ? devicePersonId.toString() : null);
                            updateObj.setAuthStatus(devicePersonId != null ? EntranceGuardPersonConstants.AuthStatusEnum.SEND_SUCCESS.getAuthStatus()
                                    : EntranceGuardPersonConstants.AuthStatusEnum.SEND_ERROR.getAuthStatus());
                            entranceGuardPersonService.updateById(updateObj);
                        }
                    }
                } else if (DeviceFaceSendCmd.TypeEnum.DEL.getType().equals(deviceFaceSendCmd.getType())) {
                    // 撤权
                    List<DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo> personFaceInfoList = deviceInfo.getPersonFaceInfoList();
                    for (DeviceFaceSendCmd.DeviceInfo.PersonFaceInfo personFaceInfo : personFaceInfoList) {

                        // 更新关系表
                        List<EntranceGuardPersonDto> entranceGuardAuthDtos = refMap.computeIfAbsent(personFaceInfo.getPersonId() + "_" + personFaceInfo.getPersonType(), k -> {
                            String[] s = k.split("_");
                            return entranceGuardPersonService.queryAuthByPersonId(Byte.valueOf(s[1]), Long.valueOf(s[0]));
                        });
                        EntranceGuardPersonDto entranceGuardPersonDto = entranceGuardAuthDtos.parallelStream()
                                .filter(o -> deviceDto.getId().equals(o.getDeviceId()))
                                .findFirst().orElse(null);

                        if (entranceGuardPersonDto != null && EntranceGuardPersonConstants.AuthTypeEnum.DEL.getAuthType().equals(entranceGuardPersonDto.getAuthType())) {
                            boolean b = entranceGuardClient.delPerson(Integer.valueOf(entranceGuardPersonDto.getIdentityAtDevice()));
                            EntranceGuardPersonDto updateObj = new EntranceGuardPersonDto();
                            updateObj.setId(entranceGuardPersonDto.getId());
                            updateObj.setAuthStatus(Boolean.TRUE.equals(b) ? EntranceGuardPersonConstants.AuthStatusEnum.SEND_SUCCESS.getAuthStatus()
                                    : EntranceGuardPersonConstants.AuthStatusEnum.SEND_ERROR.getAuthStatus());
                            entranceGuardPersonService.updateById(updateObj);
                        }
                    }
                }
            } catch (Exception e) {
                log.warn("移远 门禁设备人员授权 authPerson 异常", e);
            }
        }
    }

    /**
     * 获取或产生组
     * @param uniqueId
     * @param entranceGuardClient
     * @return
     */
    private GroupIdCls getGroup(String uniqueId, EntranceGuardClient entranceGuardClient) {
        return GROUP_CACHE.asMap().computeIfAbsent(uniqueId, k -> {
            GroupIdCls groupIdCls = new GroupIdCls();

            List<QueryGroupResp> queryGroupResps = entranceGuardClient.queryGroupList(new QueryGroupReq());
            for (QueryGroupResp queryGroupResp : queryGroupResps) {
                if ((DEFAULT_HOUSEHOLD_GROUP_NAME + uniqueId).equals(queryGroupResp.getName())
                        && "1".equals(queryGroupResp.getType())) {
                    groupIdCls.setHouseholdGroupId(queryGroupResp.getId());
                } else if ((DEFAULT_VISITOR_GROUP_NAME + uniqueId).equals(queryGroupResp.getName())
                        && "2".equals(queryGroupResp.getType())) {
                    groupIdCls .setVisitorGroupId(queryGroupResp.getId());
                }
            }

            Long deviceId = null;
            if (groupIdCls.getHouseholdGroupId() == null || groupIdCls.getVisitorGroupId() == null) {
                QueryDeviceReq queryDeviceReq = new QueryDeviceReq();
                queryDeviceReq.setDeviceLdId(uniqueId);
                List<QueryDeviceResp> queryDeviceResps = entranceGuardClient.queryDeviceList(queryDeviceReq);
                if (CollectionUtils.isNotEmpty(queryDeviceResps)) {
                    deviceId = (long)queryDeviceResps.get(0).getId();
                }
            }
            if (groupIdCls.getHouseholdGroupId() == null && deviceId != null) {
                // 添加住户组
                AddGroupReq addGroupReq = new AddGroupReq();
                addGroupReq.setName(DEFAULT_HOUSEHOLD_GROUP_NAME + uniqueId);
                addGroupReq.setType("1");
                addGroupReq.setModel("ATTENDANCE");
                addGroupReq.setDeviceIds(new Long[]{deviceId});
                Integer groupId = entranceGuardClient.addGroup(addGroupReq);
                groupIdCls.setHouseholdGroupId((long)groupId);
            }
            if (groupIdCls.getVisitorGroupId() == null && deviceId != null) {
                // 添加访客组
                AddGroupReq addGroupReq = new AddGroupReq();
                addGroupReq.setName(DEFAULT_VISITOR_GROUP_NAME + uniqueId);
                addGroupReq.setType("2");
                addGroupReq.setModel("ATTENDANCE");
                addGroupReq.setDeviceIds(new Long[]{deviceId});
                Integer groupId = entranceGuardClient.addGroup(addGroupReq);
                groupIdCls.setVisitorGroupId((long) groupId);
            }

            // 添加错误，组 id 还是可以为 null
            return groupIdCls;
        });
    }

    @Override
    public String genQrCode(DeviceQrCodeGenCmd deviceQrCodeGenCmd) {
        return null;
    }

    @Override
    public boolean syncOrRegisterDevices(ApplicationDto application, List<ApplicationOriginDevice> devices) {

        final Date currentDate = new Date();

        ApplicationAccessConf.TemplateOfQuecAttendanceConf templateOfQuecAttendanceConf = ApplicationAccessConf
                .parseExtStr(ApplicationAccessConf.TemplateOfQuecAttendanceConf.class, application.getAccessConf());
        assert templateOfQuecAttendanceConf != null;

        try {

            EntranceGuardClient entranceGuardClient = QuecEntranceClientBuilder.builder()
                    .appKey(templateOfQuecAttendanceConf.getAppKey()).appSecret(templateOfQuecAttendanceConf.getAppSecret()).baseUrl(templateOfQuecAttendanceConf.getBaseUrl())
                    .build(com.quectel.sdk.quecentrance.entranceguard.EntranceGuardClient.class);

            List<QueryDeviceResp> queryDeviceResps = entranceGuardClient.queryDeviceList(new QueryDeviceReq());
            List<DeviceDto> deviceDtos = queryDeviceResps.parallelStream()
                .filter(o -> deviceService.selectByUniqueId(o.getDeviceLdId(), application.getTenantId()) == null)
                .map(o -> {
                    DeviceDto deviceDto = new DeviceDto();
                    deviceDto.setUniqueId(o.getDeviceLdId());
                    deviceDto.setImei(o.getDeviceLdId());
                    deviceDto.setName(o.getDeviceLdId());
                    deviceDto.setIp(o.getDeviceIp());
                    deviceDto.setCreateTime(currentDate);

                    fill(deviceDto, application);
                    return deviceDto;
                }).collect(Collectors.toList());

            deviceService.saveBatch(deviceDtos);
            return true;
        } catch (Exception e) {
            log.error("移远 考勤机设备同步 syncOrRegisterDevices 异常", e);
        }
        return false;
    }

    @Override
    public boolean subscribeEvents(ApplicationDto applicationDto) {
        return true;
    }

    @Override
    public boolean initApplication(ApplicationDto application) {
        return syncOrRegisterDevices(application, null) && subscribeEvents(application);
    }

    @Override
    public boolean sendCmd(DeviceDto deviceDto, DeviceCommandTemplateDto deviceCommandTemplateDto, Map<String, Object> params, Long cmdId) {
        return true;
    }
}
